package fem2.jlinalg;

import fem2.NotImplementedError;
import inf.jlinalg.IMatrix;
import inf.jlinalg.IMatrixReadOnly;

public class COOMatrix implements IMatrix {

	private static final long serialVersionUID = 1L;

	private int nRows;
	private int nCols;
	private int nEntries;
	private int[] iRow;
	private int[] jCol;
	private double[] values;
	private int[] colPtr;

	public COOMatrix(int nRows, int nCols, int[] iRow, int[] jCol) {
		this.nRows = nRows;
		this.nCols = nCols;
		if (iRow.length != jCol.length) {
			throw new Error("iRow and jCol must have the same length.");
		} else {
			this.iRow = iRow;
			this.jCol = jCol;
			this.colPtr = new int[nCols + 1];
			this.nEntries = this.iRow.length;
			this.values = new double[this.nEntries];
			for (int i = 0; i < this.nEntries; i++) {
				colPtr[this.jCol[i] + 1]++;
			}
			for (int i = 0; i < nCols; i++) {
				colPtr[i + 1] += colPtr[i];
			}
		}
	}

	/**
	 * copy constructor
	 * 
	 * @param m
	 */
	public COOMatrix(COOMatrix m) {
		this.nRows = m.getRowCount();
		this.nCols = m.getColumnCount();
		this.nEntries = m.getNonzeros();
		this.iRow = m.getIRow().clone();
		this.jCol = m.getJCol().clone();
		this.values = m.getValues().clone();
		this.colPtr = m.colPtr.clone();
	}

	@Override
	public double get(int i, int j) {
		for (int k = colPtr[j]; k < colPtr[j + 1]; k++) {
			if (iRow[k] == i) {
				return values[k];
			}
		}
		return 0;
	}

	@Override
	public int getColumnCount() {
		return this.nCols;
	}

	@Override
	public int getRowCount() {
		return this.nRows;
	}

	@Override
	public void add(int i, int j, double value) {
		for (int k = colPtr[j]; k < colPtr[j + 1]; k++) {
			if (iRow[k] == i) {
				values[k] += value;
			}
		}
	}

	@Override
	public void set(int i, int j, double value) {
		for (int k = colPtr[j]; k < colPtr[j + 1]; k++) {
			if (iRow[k] == i) {
				values[k] = value;
			}
		}
	}

	@Override
	public void assign(IMatrixReadOnly a) {
		/*
		 * TODO
		 */
		throw new NotImplementedError();
	}

	@Override
	public Object clone() {
		return new COOMatrix(this);
	}

	/**
	 * reset all values of the matrix to zero
	 */
	public void initialize() {
		for (int i = 0; i < values.length; i++) {
			values[i] = 0;
		}
	}

	/**
	 * get number of entries
	 * 
	 * @return
	 */
	public int getNonzeros() {
		return this.nEntries;
	}

	/**
	 * get vector of associated row index
	 * 
	 * @return
	 */
	public int[] getIRow() {
		return this.iRow;
	}

	/**
	 * get vector of associated column index
	 * 
	 * @return
	 */
	public int[] getJCol() {
		return this.jCol;
	}

	/**
	 * get vector of associated values
	 * 
	 * @return
	 */
	public double[] getValues() {
		return this.values;
	}

	/**
	 * print the matrix in general form
	 */
	public void print() {
		for (int i = 0; i < nRows; i++) {
			for (int j = 0; j < nCols; j++) {
				System.out.print(get(i, j) + " ");
			}
			System.out.println();
		}
	}
}
